iT邦幫忙

2024 iThome 鐵人賽

DAY 13
0
佛心分享-SideProject30

從零開始打造超邊緣AI應用系列 第 13

Day 13 - 如何使用外部的記憶體來進行資料的存取

  • 分享至 

  • xImage
  •  

前言

昨天有說到,通常當輸入把所有的模型資料都塞進 MCU 以後,可能會發現一些尷尬的事情,可以用的資源很有可能已經到了極限,如果還要加入其他的應用就變得有困難。因此今天要來分享如何使用外部記憶體來進行應用。

先備知識

首先我們要先大概知道,大部分 MCU 是使用 Harvard 架構,這個架構大概如下表示:
https://ithelp.ithome.com.tw/upload/images/20240928/20168927GcU0NM6O3s.png

這邊可以看到 MCU 透過 Instruction Memory(通常也就是 Flash Memory)進行指令讀取(也就是讀取程式碼),接著再透過 Data Memory(通常也就是 SRAM)讀取資料進行運算。
因此通常我們看到的 MCU 使用的 Memory 資源圖(如下圖所示)中的 ROM 和 RAM 分別指的就是上面的 Instruction Memory 和 Data Memory(這邊的 ROM 和 RAM 命名不一定,要看 Linker script 是怎麼寫的):
https://ithelp.ithome.com.tw/upload/images/20240928/20168927oKNFTTba8C.png
而這邊的資源通常都是指 CPU 上面原生的 Memory 資源,例如上面的圖就顯示了 U585 可以使用的原生 Internal Flash 和 Internal SRAM 資源,分別為 2MB 和 768KB,如果想要增加這個部分的資源,就需要透過加入外部的記憶特進行運算。

這邊可以先簡單看一下底下的 U5 系列的系統架構圖:
https://ithelp.ithome.com.tw/upload/images/20240928/20168927QDJVGTYZd4.png
這邊可以看到除了原生擁有的 Internal Flash 和 SRAM 以外,右下角還提供了 OCTOSPI1 和 OCTOSPI2 可以讓你額外連接 External Flash / External SRAM。

而在 B-U585I-IOT2A 這塊開發板上,已經預先幫你先上好了一塊 SRAM 和 FLASH,分別就是掛在 OCTOSPI1 和 OCTOSPI2 上,因此可以直接進行使用。

使用 External Memory

使用 External Memory 來進行 EdgeAI 的操作分成兩個部分,一個是先要實現“如何透過連接介面(OCTOSPI)對連接的 External memory 進行控制”,另一個則是要怎麼把你想放進去得資料丟到相應的地區進行使用,而使用這塊開發板很好的事情是,幾乎兩個部分都幫你做好非常多的準備了,你只要稍微修改部分資料就能夠快速進行應用。

這邊就直接透過上次用到一半的 CubeMX 專案進行測試(Efficient Net 那個),來看看要怎麼使用。
首先可以到 X-AI Model 的部分進行上次沒操作到的 Memory 設定,首先是 RAM 的部分,把所有的 Activation buffer 都放到 External SRAM 裡面進行操作:
https://ithelp.ithome.com.tw/upload/images/20240928/20168927ZtKDpdLwo7.png
然後是 Flash 的部分,把 Network Data 生成獨立的 binary 檔案以後,放到 External Flash 裡面:
https://ithelp.ithome.com.tw/upload/images/20240928/20168927IKfCjAbHdN.png
這邊可以看到地址填分別是 SRAM: 0x90000000 和 FLASH: 0x70000000,這個部分可以參考 U5 系列的 Datasheet:
https://ithelp.ithome.com.tw/upload/images/20240928/20168927ztM7wcKup1.png

接著可以直接按確認,CubeMX 就會重新計算整個網路的資訊並且設定,然後再重新生成 Code。
這時候可以在生成的 Code 裡面可以看到幾個檔案(應該會在 Driver/BSP 資料夾底下):
https://ithelp.ithome.com.tw/upload/images/20240928/201689271qIaecysgU.png
這幾個檔案就是 ST 預先寫好給 B-U585I-IOT2A 的 BSP 檔案(Board support package),這幾個檔案就是操作開發板上 External Flash / External SRAM 的 Driver,這個直接省去需要自己看著 Datasheet 刻程式的時間成本。

這時候我們回去看 void MX_X_CUBE_AI_Init(void)(在app_x-cube-ai.c底下),理應會看到以下的程式碼:

void MX_X_CUBE_AI_Init(void)
{
    MX_UARTx_Init();
    // 初始化透過 OCTOSPI 連接的 External NOR FLASH
    BSP_OSPI_NOR_Init_t ospiInit;
    ospiInit.InterfaceMode=BSP_OSPI_NOR_OPI_MODE;
    ospiInit.TransferRate=BSP_OSPI_NOR_STR_TRANSFER;
    BSP_OSPI_NOR_Init(0, &ospiInit);
    // 使用方式是利用 Memory Map,這樣就會把 Memory 映射到 0x70000000
    BSP_OSPI_NOR_EnableMemoryMappedMode(0);
    // 初始化透過 OCTOSPI 連接的 External SRAM
    BSP_OSPI_RAM_Init(0);
    // 使用方式是利用 Memory Map,這樣就會把 Memory 映射到 0x90000000
    BSP_OSPI_RAM_EnableMemoryMappedMode(0);
    aiSystemPerformanceInit();
    /* USER CODE BEGIN 5 */
    /* USER CODE END 5 */
}

如果沒有,可能需要自己補一下缺失的地方XD 因為這工具還是有一些 bug 要克服,可以看上面我的註解大概了解這些 code 分別是在做什麼。

當加好這些東西以後,可能還需要多做一件事情(不確定,因為感覺也是工具的 bug?),就是透過修改 Linker script 去把 activation buffer 指定到 External SRAM。
首先打開 STM32U585xx_FLASH.ld,修改開頭加入 External Memory:

/* Memories definition */
MEMORY
{
  RAM	(xrw)	: ORIGIN = 0x20000000,	LENGTH = 768K
  ROM	(rx)	: ORIGIN = 0x08000000,	LENGTH = 2048K
  EXTSRAM (xrw) : ORIGIN = 0x90000000, LENGTH = 32M
  SRAM4 (xrw)   : ORIGIN = 0x28000000,  LENGTH = 16K
}

然後再加入 activation buffer 的區段:

  .activations :
  {
    . = ALIGN(8);
    __activations_start__ = .;
    *(.activations)
    . = ALIGN(8);
    __activations_end__ = .;
  } >EXTSRAM

最後在 app_x-cube-ai.c 中把 pool0 指定到剛剛設定的區段:

AI_ALIGNED(32)
static uint8_t pool0[AI_NETWORK_DATA_ACTIVATION_1_SIZE] __attribute__((section(".activations")));
ai_handle data_activations0[] = {pool0};

完成以上的步驟就可以開始燒錄程式了,但還記得剛剛在 External Flash 上我們有把 Network data 分別成一個單獨的檔案,應該可以在專案根目錄底下看到這個檔案 network_data.bin,這個也需要單獨燒到 external flash 上,還好 ST 的工具也十分齊全,透過 ST32Cube-Programmer 就能輕鬆做到。
首先打開 Programmer,找到 External Loader(左下角第三個選項),然後選擇 B-U585I-IOT2A 這塊板子,並且勾選起來,如下圖:
https://ithelp.ithome.com.tw/upload/images/20240928/20168927BYz70DvJyT.png
接著再到 External Loader 中(左上選單第二個選項)選擇剛剛的 network_data.bin 檔案,並且燒錄,如下圖:
https://ithelp.ithome.com.tw/upload/images/20240928/20168927NaIioG9N5T.png
這樣 network_data.bin 就燒錄完成了,接著再正常燒錄程式剩餘編譯出來的程式就可以。
另外在編譯完成的時候,其實也可以注意到,相比於一開始的 Memory 使用量,這邊跑出來的結果差非常多:
https://ithelp.ithome.com.tw/upload/images/20240928/20168927bmZOAnFxty.png
這就是把資料分開燒錄以及加入External SRAM 的結果。

接著順利都燒錄進去以後,應該可以看到以下的結果:
https://ithelp.ithome.com.tw/upload/images/20240928/20168927qvvhBOILdL.png

那到這邊就完成了把 Network data 和 推理使用的 buffer 放到 External memory 的過程。

但...魔鬼藏在細節裡,難道這樣就結束了嗎?如果仔細看可能會發現好像有不少 trade-off 呢!這部分之後再來提~今天就先到這裡,下期再會~


上一篇
Day12- 驗證 Application 的架構
系列文
從零開始打造超邊緣AI應用13
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言